/**
 * Copyright 2010 Zhou Zhao
 *
 * This file is part of FPGA compiler for EE680 USC
 *
   FPGA compiler is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   FPGA compiler is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * The code in class of Netlist is used for importing netlist from txt file.
 * The code uses regular expression to import netlist, which simplifies the format
 * of each statement. Although there are some blanks between signal name and pin
 * number, code can also read in the netlist. The output of this importing process
 * is a map data structure, which has key and value pair. Each key is a signal
 * name with corresponding set of switchboxes stored in value.
 *
 */

package edu.usc.ee.java;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.MatchResult;

/**
 *
 * @author zhouzhao
 * @version 1.0
 */

public class Netlist{

    private int x;
    private int y;
    private int n;
    private int k;

    public Netlist(int n, int k)
    {
        this.x = 0;
        this.y = 0;
        this.n = n;
        this.k = k;

    }

    public int getx()
    {
        return this.x;
    }

    public void setx(int x)
    {
        this.x=x;
    }

    public int gety()
    {
        return this.y;
    }

    public void sety(int y)
    {
        this.y = y;
    }

    public int getn()
    {
        return this.n;
    }

    public int getk()
    {
        return this.k;
    }

    public void pin2coord(int n, int k, int pin)
    {
        if(pin>=1 && pin<=n*k*4){
            switch((pin-1)/(n*k)){
            case 0: x=((pin-1)/k+1)*2-1; y=0; break;
            case 1: x=2*n; y=(((pin-n*k)-1)/k+1)*2-1; break;
            case 2: x=2*n-(((pin-n*k*2)-1)/k+1)*2+1; y=2*n; break;
            case 3: x=0; y=2*n-(((pin-n*k*3)-1)/k+1)*2+1; break;
            default: x=0; y=0;
            }
//            System.out.println("x="+x+" y="+y);
        }else{
            System.out.println("Invalid pin number.");
        }
    }

    public void array2coord(int n, int row, int col)
    {
         if(row>=1 && row<=n){
            if(col>=1 && col<=n){
                x = col*2-1;
                y = row*2-1;
//                System.out.println("x="+x+" y="+y);
            }else{
                System.out.println("Invalid column number");
            }
        }else{
            System.out.println("Invalid row number");
        }
    }
    
    public Map<Integer, Set<switchBox>> netlistProcess(switchBox[][] array)throws IOException{
        // TODO code application logic here
        Map<Integer, Set<switchBox>> m = new HashMap<Integer, Set<switchBox>>();

        BufferedReader inputStream = null;
        PrintWriter outputStream = null;

        Integer sig;
        Integer node;

        Integer input_sig1;
        Integer input_sig2;
        Integer input_sig3;
        Integer input_sig4;
        Integer output_sig;
        Integer row;
        Integer column;

        try{
            inputStream = new BufferedReader(new FileReader("intraFPGA_Routing_6.txt"));
//            outputStream = new PrintWriter(new FileWriter("lineoutput.txt"));

            String line;
            while((line = inputStream.readLine()) != null){
//                outputStream.println(line);

                boolean in = line.contains("INPUT_SIGNAL"); //input signal
                boolean out = line.contains("OUTPUT_SIGNAL"); //output signal

                boolean not = line.contains("NOT"); //one input gate
                
                boolean nand2 = line.contains("NAND2"); //two input gate
                boolean nor2 = line.contains("NOR2");
                boolean xor2 = line.contains("XOR2");
                boolean dff = line.contains("DFF");

                boolean nand3 = line.contains("NAND3"); //three input gate
                boolean nor3 = line.contains("NOR3");
                boolean xor3 = line.contains("XOR3");

                boolean nand4 = line.contains("NAND4"); //four input gate
                boolean nor4 = line.contains("NOR4");
                boolean xor4 = line.contains("XOR4");

              if(in){
                      Scanner sc = new Scanner(line); //INPUT_SIGNAL(G5) 21
                      sc.findInLine("\\s*INPUT_SIGNAL\\s*\\(\\s*G(\\d+)\\s*\\)\\s*(\\d+)");
                      MatchResult result = sc.match();

                      sig = new Integer(result.group(1));
                      node = new Integer(result.group(2));
                      pin2coord(n, k, node);

                      switch(x){
                          case 0: array[x][y].addWest(sig); break;
                          case 6: array[x][y].addEast(sig); break;
                          default: break;
                      }

                      switch(y){
                          case 0: array[x][y].addNorth(sig); break;
                          case 6: array[x][y].addSouth(sig); break;
                          default: break;
                      }

                      if(m.containsKey(sig)){
                          m.get(sig).add(array[x][y]);
                      }
                      else{
                          HashSet<switchBox> nodes = new HashSet<switchBox>();
                          nodes.add(array[x][y]);
                          m.put(sig, nodes);
                      }
                      sc.close();
              }

              if(out){
                      Scanner sc = new Scanner(line);
                      //OUTPUT_SIGNAL (G16) 12
                      sc.findInLine("\\s*OUTPUT_SIGNAL\\s*\\(\\s*G(\\d+)\\s*\\)\\s*(\\d+)");
                      MatchResult result = sc.match();

                      sig = new Integer(result.group(1));
                      node = new Integer(result.group(2));
                      pin2coord(n, k, node);

                      switch(x){
                          case 0: array[x][y].addWest(sig); break;
                          case 6: array[x][y].addEast(sig); break;
                          default: break;
                      }

                      switch(y){
                          case 0: array[x][y].addNorth(sig); break;
                          case 6: array[x][y].addSouth(sig); break;
                          default: break;
                      }

                      if(m.containsKey(sig)){
                          m.get(sig).add(array[x][y]);
                      }
                      else{
                          HashSet<switchBox> nodes = new HashSet<switchBox>();
                          nodes.add(array[x][y]);
                          m.put(sig, nodes);
                      }

                      sc.close();
//                      System.out.println(m);

                  }
                      
                if(nand2 || nor2 || xor2 || dff){
                    
                          Scanner sc = new Scanner(line);
                          //G8 = NAND(G1, G3) 1 4
                          sc.findInLine("\\s*G(\\d+)\\s*=\\s*\\w+\\(\\s*G(\\d+)\\s*,\\s*G(\\d+)\\)\\s*(\\d+)\\s*(\\d+)");
                          MatchResult result = sc.match();

                          output_sig = new Integer(result.group(1));
                          input_sig1 = new Integer(result.group(2));
                          input_sig2 = new Integer(result.group(3));
                          row = new Integer(result.group(4));
                          column = new Integer(result.group(5));

                          array2coord(n, row, column);

                          array[x+1][y].addWest(output_sig);
                          array[x-1][y].addEast(input_sig1);
                          array[x-1][y].addEast(input_sig2);

                          if(m.containsKey(output_sig)){
                              m.get(output_sig).add(array[x+1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x+1][y]);
                              m.put(output_sig, nodes);
                          }

                          if(m.containsKey(input_sig1)){
                              m.get(input_sig1).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig1, nodes);
                          }

                          if(m.containsKey(input_sig2)){
                              m.get(input_sig2).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig2, nodes);
                          }
                          sc.close();
//                          System.out.println(m);
                      }

                if(nand3 || nor3 || xor3){

                          Scanner sc = new Scanner(line);
                          //G28 = NAND3(G21, G22, G23) 3 2
                          sc.findInLine("\\s*G(\\d+)\\s*=\\s*\\w+\\(\\s*G(\\d+)\\s*,\\s*G(\\d+)\\s*,\\s*G(\\d+)\\s*\\)\\s*(\\d+)\\s*(\\d+)");
                          MatchResult result = sc.match();

                          output_sig = new Integer(result.group(1));
                          input_sig1 = new Integer(result.group(2));
                          input_sig2 = new Integer(result.group(3));
                          input_sig3 = new Integer(result.group(4));
                          row = new Integer(result.group(5));
                          column = new Integer(result.group(6));

                          array2coord(n, row, column);
                          array[x+1][y].addWest(output_sig);
                          array[x-1][y].addEast(input_sig1);
                          array[x-1][y].addEast(input_sig2);
                          array[x-1][y].addEast(input_sig3);

                          if(m.containsKey(output_sig)){
                              m.get(output_sig).add(array[x+1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x+1][y]);
                              m.put(output_sig, nodes);
                          }

                          if(m.containsKey(input_sig1)){
                              m.get(input_sig1).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig1, nodes);
                          }

                          if(m.containsKey(input_sig2)){
                              m.get(input_sig2).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig2, nodes);
                          }

                          if(m.containsKey(input_sig3)){
                              m.get(input_sig3).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig3, nodes);
                          }
                          sc.close();
                      }

                if(nand4 || nor4 || xor4){

                          Scanner sc = new Scanner(line);
                          //G33 = NAND4(G6, G29, G28, G27) 3 3
                          sc.findInLine("\\s*G(\\d+)\\s*=\\s*\\w+\\(\\s*G(\\d+)\\s*,\\s*G(\\d+)\\s*,\\s*G(\\d+)\\s*,\\s*G(\\d+)\\s*\\)\\s*(\\d+)\\s*(\\d+)");
                          MatchResult result = sc.match();

                          output_sig = new Integer(result.group(1));
                          input_sig1 = new Integer(result.group(2));
                          input_sig2 = new Integer(result.group(3));
                          input_sig3 = new Integer(result.group(4));
                          input_sig4 = new Integer(result.group(5));
                          row = new Integer(result.group(6));
                          column = new Integer(result.group(7));

                          array2coord(n, row, column);
                          array[x+1][y].addWest(output_sig);
                          array[x-1][y].addEast(input_sig1);
                          array[x-1][y].addEast(input_sig2);
                          array[x-1][y].addEast(input_sig3);
                          array[x-1][y].addEast(input_sig4);

                          if(m.containsKey(output_sig)){
                              m.get(output_sig).add(array[x+1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x+1][y]);
                              m.put(output_sig, nodes);
                          }

                          if(m.containsKey(input_sig1)){
                              m.get(input_sig1).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig1, nodes);
                          }

                          if(m.containsKey(input_sig2)){
                              m.get(input_sig2).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig2, nodes);
                          }

                          if(m.containsKey(input_sig3)){
                              m.get(input_sig3).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig3, nodes);
                          }

                          if(m.containsKey(input_sig4)){
                              m.get(input_sig4).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig4, nodes);
                          }
                          sc.close();
                      }

                  if(not){                

                          Scanner sc = new Scanner(line);
                          //G2 = NOT(G1) 1  2
                          sc.findInLine("\\s*G(\\d+)\\s*=\\s*\\w+\\(\\s*G(\\d+)\\s*\\)\\s*(\\d+)\\s*(\\d+)");
                          MatchResult result = sc.match();

                          output_sig = new Integer(result.group(1));
                          input_sig1 = new Integer(result.group(2));
                          
                          row = new Integer(result.group(3));
                          column = new Integer(result.group(4));

                          array2coord(n, row, column);
                          array[x+1][y].addWest(output_sig);
                          array[x-1][y].addEast(input_sig1);

                          if(m.containsKey(output_sig)){
                              m.get(output_sig).add(array[x+1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x+1][y]);
                              m.put(output_sig, nodes);
                          }

                          if(m.containsKey(input_sig1)){
                              m.get(input_sig1).add(array[x-1][y]);
                          }
                          else{
                              HashSet<switchBox> nodes = new HashSet<switchBox>();
                              nodes.add(array[x-1][y]);
                              m.put(input_sig1, nodes);
                          }
                          sc.close();              
                  }
            }
        }

        finally{
            if(inputStream != null){
                inputStream.close();
            }
            if(outputStream != null){
                outputStream.close();
            }
        }

        return m;
    }
}
